"""
风险监控器

提供实时风险监控、风险指标计算、风险告警等功能。
"""

from typing import Dict, List, Optional, Any, Callable
from datetime import datetime, timedelta
from dataclasses import dataclass, field
from enum import Enum
import asyncio
import math
import statistics
from decimal import Decimal

from .monitor import BaseMonitor, MonitorConfig, MonitorType, MonitorMetrics, MonitorLevel


class RiskLevel(Enum):
    """风险等级"""
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    CRITICAL = "critical"


@dataclass
class RiskMetrics:
    """风险指标"""
    timestamp: datetime = field(default_factory=datetime.now)
    
    # 市场风险指标
    var_1d: float = 0.0  # 1日VaR
    var_5d: float = 0.0  # 5日VaR
    cvar_1d: float = 0.0  # 1日CVaR
    cvar_5d: float = 0.0  # 5日CVaR
    beta: float = 0.0  # Beta系数
    correlation: float = 0.0  # 与基准相关性
    
    # 信用风险指标
    concentration_risk: float = 0.0  # 集中度风险
    counterparty_exposure: float = 0.0  # 交易对手风险敞口
    credit_rating: str = ""  # 信用评级
    
    # 流动性风险指标
    liquidity_ratio: float = 0.0  # 流动性比率
    bid_ask_spread: float = 0.0  # 买卖价差
    market_impact: float = 0.0  # 市场冲击
    
    # 操作风险指标
    system_uptime: float = 0.0  # 系统正常运行时间
    error_rate: float = 0.0  # 错误率
    latency: float = 0.0  # 延迟
    
    # 组合风险指标
    portfolio_volatility: float = 0.0  # 组合波动率
    max_drawdown: float = 0.0  # 最大回撤
    leverage_ratio: float = 0.0  # 杠杆比率
    position_concentration: float = 0.0  # 持仓集中度
    
    # 风险等级
    overall_risk_level: RiskLevel = RiskLevel.LOW
    
    # 自定义风险指标
    custom_metrics: Dict[str, Any] = field(default_factory=dict)


@dataclass
class RiskLimit:
    """风险限额"""
    name: str
    limit_type: str  # 'absolute', 'percentage', 'ratio'
    limit_value: float
    current_value: float = 0.0
    utilization: float = 0.0  # 使用率
    breach_count: int = 0
    last_breach_time: Optional[datetime] = None
    is_breached: bool = False


class MarketRiskMonitor(BaseMonitor):
    """市场风险监控器"""
    
    def __init__(self, config: Optional[MonitorConfig] = None):
        """初始化市场风险监控器"""
        if config is None:
            config = MonitorConfig(
                monitor_id="market_risk",
                monitor_type=MonitorType.RISK,
                name="市场风险监控",
                description="监控VaR、CVaR、Beta等市场风险指标",
                interval=30.0
            )
        super().__init__(config)
        
        # 历史数据
        self._price_history: Dict[str, List[float]] = {}
        self._return_history: Dict[str, List[float]] = {}
        self._portfolio_values: List[float] = []
        self._benchmark_returns: List[float] = []
    
    async def check(self) -> MonitorMetrics:
        """检查市场风险"""
        try:
            # 计算风险指标
            var_1d = self._calculate_var(confidence=0.95, days=1)
            var_5d = self._calculate_var(confidence=0.95, days=5)
            cvar_1d = self._calculate_cvar(confidence=0.95, days=1)
            cvar_5d = self._calculate_cvar(confidence=0.95, days=5)
            beta = self._calculate_beta()
            correlation = self._calculate_correlation()
            
            # 创建风险指标
            risk_metrics = RiskMetrics(
                var_1d=var_1d,
                var_5d=var_5d,
                cvar_1d=cvar_1d,
                cvar_5d=cvar_5d,
                beta=beta,
                correlation=correlation
            )
            
            # 更新监控指标
            metrics = MonitorMetrics(self.config.monitor_id)
            metrics.update_metrics({
                'var_1d': var_1d,
                'var_5d': var_5d,
                'cvar_1d': cvar_1d,
                'cvar_5d': cvar_5d,
                'beta': beta,
                'correlation': correlation,
                'portfolio_volatility': self._calculate_portfolio_volatility()
            })
            
            # 检查风险阈值
            await self._check_market_risk_thresholds(risk_metrics)
            
            return metrics
            
        except Exception as e:
            self._emit_event(
                "market_risk_check_error",
                MonitorLevel.ERROR,
                f"市场风险检查失败: {e}"
            )
            return MonitorMetrics(self.config.monitor_id)
    
    def update_price_data(self, symbol: str, price: float) -> None:
        """更新价格数据"""
        if symbol not in self._price_history:
            self._price_history[symbol] = []
            self._return_history[symbol] = []
        
        self._price_history[symbol].append(price)
        
        # 计算收益率
        if len(self._price_history[symbol]) > 1:
            prev_price = self._price_history[symbol][-2]
            if prev_price > 0:
                return_rate = (price - prev_price) / prev_price
                self._return_history[symbol].append(return_rate)
        
        # 保留最近1000个数据点
        if len(self._price_history[symbol]) > 1000:
            self._price_history[symbol].pop(0)
        if len(self._return_history[symbol]) > 1000:
            self._return_history[symbol].pop(0)
    
    def update_portfolio_value(self, value: float) -> None:
        """更新组合价值"""
        self._portfolio_values.append(value)
        if len(self._portfolio_values) > 1000:
            self._portfolio_values.pop(0)
    
    def update_benchmark_return(self, return_rate: float) -> None:
        """更新基准收益率"""
        self._benchmark_returns.append(return_rate)
        if len(self._benchmark_returns) > 1000:
            self._benchmark_returns.pop(0)
    
    def _calculate_var(self, confidence: float = 0.95, days: int = 1) -> float:
        """计算VaR (Value at Risk)"""
        if len(self._portfolio_values) < 2:
            return 0.0
        
        # 计算组合收益率
        returns = []
        for i in range(1, len(self._portfolio_values)):
            prev_value = self._portfolio_values[i-1]
            if prev_value > 0:
                return_rate = (self._portfolio_values[i] - prev_value) / prev_value
                returns.append(return_rate)
        
        if not returns:
            return 0.0
        
        # 计算VaR
        var_percentile = (1 - confidence) * 100
        sorted_returns = sorted(returns)
        percentile_index = int(len(sorted_returns) * var_percentile / 100)
        var = sorted_returns[min(percentile_index, len(sorted_returns) - 1)]
        
        # 调整为指定天数
        var_adjusted = var * math.sqrt(days)
        
        return abs(var_adjusted)
    
    def _calculate_cvar(self, confidence: float = 0.95, days: int = 1) -> float:
        """计算CVaR (Conditional Value at Risk)"""
        if len(self._portfolio_values) < 2:
            return 0.0
        
        # 计算组合收益率
        returns = []
        for i in range(1, len(self._portfolio_values)):
            prev_value = self._portfolio_values[i-1]
            if prev_value > 0:
                return_rate = (self._portfolio_values[i] - prev_value) / prev_value
                returns.append(return_rate)
        
        if not returns:
            return 0.0
        
        # 计算CVaR
        var_percentile = (1 - confidence) * 100
        sorted_returns = sorted(returns)
        percentile_index = int(len(sorted_returns) * var_percentile / 100)
        var_threshold = sorted_returns[min(percentile_index, len(sorted_returns) - 1)]
        
        # 计算超过VaR阈值的平均损失
        tail_losses = [r for r in returns if r <= var_threshold]
        if len(tail_losses) > 0:
            cvar = sum(tail_losses) / len(tail_losses)
        else:
            cvar = var_threshold
        
        # 调整为指定天数
        cvar_adjusted = cvar * math.sqrt(days)
        
        return abs(cvar_adjusted)
    
    def _calculate_beta(self) -> float:
        """计算Beta系数"""
        if len(self._portfolio_values) < 2 or len(self._benchmark_returns) < 2:
            return 0.0
        
        # 计算组合收益率
        portfolio_returns = []
        for i in range(1, len(self._portfolio_values)):
            prev_value = self._portfolio_values[i-1]
            if prev_value > 0:
                return_rate = (self._portfolio_values[i] - prev_value) / prev_value
                portfolio_returns.append(return_rate)
        
        if not portfolio_returns:
            return 0.0
        
        # 确保数据长度一致
        min_length = min(len(portfolio_returns), len(self._benchmark_returns))
        if min_length < 2:
            return 0.0
        
        portfolio_returns = portfolio_returns[-min_length:]
        benchmark_returns = self._benchmark_returns[-min_length:]
        
        # 计算Beta
        # 计算协方差和方差
        portfolio_mean = sum(portfolio_returns) / len(portfolio_returns)
        benchmark_mean = sum(benchmark_returns) / len(benchmark_returns)
        
        covariance = sum((p - portfolio_mean) * (b - benchmark_mean)
                        for p, b in zip(portfolio_returns, benchmark_returns)) / len(portfolio_returns)
        benchmark_variance = sum((b - benchmark_mean) ** 2 for b in benchmark_returns) / len(benchmark_returns)
        
        if benchmark_variance == 0:
            return 0.0
        
        beta = covariance / benchmark_variance
        return beta
    
    def _calculate_correlation(self) -> float:
        """计算与基准的相关性"""
        if len(self._portfolio_values) < 2 or len(self._benchmark_returns) < 2:
            return 0.0
        
        # 计算组合收益率
        portfolio_returns = []
        for i in range(1, len(self._portfolio_values)):
            prev_value = self._portfolio_values[i-1]
            if prev_value > 0:
                return_rate = (self._portfolio_values[i] - prev_value) / prev_value
                portfolio_returns.append(return_rate)
        
        if not portfolio_returns:
            return 0.0
        
        # 确保数据长度一致
        min_length = min(len(portfolio_returns), len(self._benchmark_returns))
        if min_length < 2:
            return 0.0
        
        portfolio_returns = portfolio_returns[-min_length:]
        benchmark_returns = self._benchmark_returns[-min_length:]
        
        # 计算相关系数
        portfolio_mean = sum(portfolio_returns) / len(portfolio_returns)
        benchmark_mean = sum(benchmark_returns) / len(benchmark_returns)
        
        numerator = sum((p - portfolio_mean) * (b - benchmark_mean)
                       for p, b in zip(portfolio_returns, benchmark_returns))
        
        portfolio_variance = sum((p - portfolio_mean) ** 2 for p in portfolio_returns)
        benchmark_variance = sum((b - benchmark_mean) ** 2 for b in benchmark_returns)
        
        denominator = math.sqrt(portfolio_variance * benchmark_variance)
        
        if denominator == 0:
            return 0.0
        
        correlation = numerator / denominator
        return correlation if not math.isnan(correlation) else 0.0
    
    def _calculate_portfolio_volatility(self) -> float:
        """计算组合波动率"""
        if len(self._portfolio_values) < 2:
            return 0.0
        
        # 计算组合收益率
        returns = []
        for i in range(1, len(self._portfolio_values)):
            prev_value = self._portfolio_values[i-1]
            if prev_value > 0:
                return_rate = (self._portfolio_values[i] - prev_value) / prev_value
                returns.append(return_rate)
        
        if not returns:
            return 0.0
        
        # 计算波动率
        mean_return = sum(returns) / len(returns)
        variance = sum((r - mean_return) ** 2 for r in returns) / len(returns)
        volatility = math.sqrt(variance)
        return volatility
    
    async def _check_market_risk_thresholds(self, risk_metrics: RiskMetrics) -> None:
        """检查市场风险阈值"""
        # VaR告警
        var_threshold = self.config.thresholds.get('var_1d', 0.05)  # 5%
        if risk_metrics.var_1d > var_threshold:
            self._emit_event(
                "high_var",
                MonitorLevel.WARNING,
                f"1日VaR过高: {risk_metrics.var_1d:.2%}",
                {"var_1d": risk_metrics.var_1d, "threshold": var_threshold}
            )
        
        # Beta告警
        beta_threshold = self.config.thresholds.get('beta', 1.5)
        if abs(risk_metrics.beta) > beta_threshold:
            self._emit_event(
                "high_beta",
                MonitorLevel.WARNING,
                f"Beta系数过高: {risk_metrics.beta:.2f}",
                {"beta": risk_metrics.beta, "threshold": beta_threshold}
            )


class PositionRiskMonitor(BaseMonitor):
    """持仓风险监控器"""
    
    def __init__(self, config: Optional[MonitorConfig] = None):
        """初始化持仓风险监控器"""
        if config is None:
            config = MonitorConfig(
                monitor_id="position_risk",
                monitor_type=MonitorType.RISK,
                name="持仓风险监控",
                description="监控持仓集中度、杠杆比率等持仓风险",
                interval=60.0
            )
        super().__init__(config)
        
        # 持仓数据
        self._positions: Dict[str, Dict[str, Any]] = {}
        self._total_portfolio_value: float = 0.0
        self._risk_limits: Dict[str, RiskLimit] = {}
    
    async def check(self) -> MonitorMetrics:
        """检查持仓风险"""
        try:
            # 计算风险指标
            concentration_risk = self._calculate_concentration_risk()
            leverage_ratio = self._calculate_leverage_ratio()
            position_concentration = self._calculate_position_concentration()
            
            # 创建风险指标
            risk_metrics = RiskMetrics(
                concentration_risk=concentration_risk,
                leverage_ratio=leverage_ratio,
                position_concentration=position_concentration
            )
            
            # 更新监控指标
            metrics = MonitorMetrics(self.config.monitor_id)
            metrics.update_metrics({
                'concentration_risk': concentration_risk,
                'leverage_ratio': leverage_ratio,
                'position_concentration': position_concentration,
                'total_positions': len(self._positions),
                'portfolio_value': self._total_portfolio_value
            })
            
            # 检查风险限额
            await self._check_risk_limits()
            
            # 检查持仓风险阈值
            await self._check_position_risk_thresholds(risk_metrics)
            
            return metrics
            
        except Exception as e:
            self._emit_event(
                "position_risk_check_error",
                MonitorLevel.ERROR,
                f"持仓风险检查失败: {e}"
            )
            return MonitorMetrics(self.config.monitor_id)
    
    def update_position(self, symbol: str, quantity: float, price: float, **kwargs) -> None:
        """更新持仓信息"""
        self._positions[symbol] = {
            'quantity': quantity,
            'price': price,
            'market_value': quantity * price,
            'timestamp': datetime.now(),
            **kwargs
        }
        
        # 更新总组合价值
        self._total_portfolio_value = sum(
            pos['market_value'] for pos in self._positions.values()
        )
    
    def add_risk_limit(self, limit: RiskLimit) -> None:
        """添加风险限额"""
        self._risk_limits[limit.name] = limit
    
    def _calculate_concentration_risk(self) -> float:
        """计算集中度风险"""
        if not self._positions or self._total_portfolio_value == 0:
            return 0.0
        
        # 计算最大单一持仓占比
        max_position_weight = 0.0
        for position in self._positions.values():
            weight = position['market_value'] / self._total_portfolio_value
            max_position_weight = max(max_position_weight, weight)
        
        return max_position_weight
    
    def _calculate_leverage_ratio(self) -> float:
        """计算杠杆比率"""
        if not self._positions:
            return 0.0
        
        # 计算总敞口
        total_exposure = sum(
            abs(pos['market_value']) for pos in self._positions.values()
        )
        
        if self._total_portfolio_value == 0:
            return 0.0
        
        leverage_ratio = total_exposure / abs(self._total_portfolio_value)
        return leverage_ratio
    
    def _calculate_position_concentration(self) -> float:
        """计算持仓集中度 (HHI指数)"""
        if not self._positions or self._total_portfolio_value == 0:
            return 0.0
        
        # 计算HHI指数
        hhi = 0.0
        for position in self._positions.values():
            weight = position['market_value'] / self._total_portfolio_value
            hhi += weight ** 2
        
        return hhi
    
    async def _check_risk_limits(self) -> None:
        """检查风险限额"""
        for limit_name, limit in self._risk_limits.items():
            # 更新当前值
            if limit_name == "max_position_weight":
                limit.current_value = self._calculate_concentration_risk()
            elif limit_name == "leverage_ratio":
                limit.current_value = self._calculate_leverage_ratio()
            elif limit_name == "position_concentration":
                limit.current_value = self._calculate_position_concentration()
            
            # 计算使用率
            if limit.limit_value > 0:
                limit.utilization = limit.current_value / limit.limit_value
            
            # 检查是否违反限额
            if limit.current_value > limit.limit_value:
                if not limit.is_breached:
                    limit.breach_count += 1
                    limit.last_breach_time = datetime.now()
                    limit.is_breached = True
                    
                    self._emit_event(
                        "risk_limit_breach",
                        MonitorLevel.ERROR,
                        f"风险限额违反: {limit_name} = {limit.current_value:.4f} > {limit.limit_value:.4f}",
                        {
                            "limit_name": limit_name,
                            "current_value": limit.current_value,
                            "limit_value": limit.limit_value,
                            "utilization": limit.utilization
                        }
                    )
            else:
                limit.is_breached = False
    
    async def _check_position_risk_thresholds(self, risk_metrics: RiskMetrics) -> None:
        """检查持仓风险阈值"""
        # 集中度风险告警
        concentration_threshold = self.config.thresholds.get('concentration_risk', 0.3)  # 30%
        if risk_metrics.concentration_risk > concentration_threshold:
            self._emit_event(
                "high_concentration_risk",
                MonitorLevel.WARNING,
                f"持仓集中度过高: {risk_metrics.concentration_risk:.2%}",
                {"concentration_risk": risk_metrics.concentration_risk, "threshold": concentration_threshold}
            )
        
        # 杠杆比率告警
        leverage_threshold = self.config.thresholds.get('leverage_ratio', 3.0)
        if risk_metrics.leverage_ratio > leverage_threshold:
            self._emit_event(
                "high_leverage",
                MonitorLevel.WARNING,
                f"杠杆比率过高: {risk_metrics.leverage_ratio:.2f}",
                {"leverage_ratio": risk_metrics.leverage_ratio, "threshold": leverage_threshold}
            )


class RiskMonitor:
    """
    风险监控器管理器
    
    管理和协调各种风险监控器。
    """
    
    def __init__(self):
        """初始化风险监控器管理器"""
        self.market_risk_monitor = MarketRiskMonitor()
        self.position_risk_monitor = PositionRiskMonitor()
        
        self._monitors = {
            'market_risk': self.market_risk_monitor,
            'position_risk': self.position_risk_monitor
        }
        
        # 风险等级评估
        self._risk_weights = {
            'market_risk': 0.4,
            'position_risk': 0.3,
            'liquidity_risk': 0.2,
            'operational_risk': 0.1
        }
    
    def start_all(self) -> None:
        """启动所有风险监控器"""
        for monitor in self._monitors.values():
            monitor.start()
    
    def stop_all(self) -> None:
        """停止所有风险监控器"""
        for monitor in self._monitors.values():
            monitor.stop()
    
    def get_risk_summary(self) -> Dict[str, Any]:
        """获取风险摘要"""
        summary = {}
        
        for name, monitor in self._monitors.items():
            summary[name] = {
                'status': monitor.status.value,
                'metrics': monitor.metrics.metrics,
                'last_check': monitor.last_check_time.isoformat() if monitor.last_check_time else None,
                'check_count': monitor.check_count,
                'error_count': monitor.error_count
            }
        
        # 计算整体风险等级
        overall_risk_level = self._calculate_overall_risk_level()
        summary['overall_risk_level'] = overall_risk_level.value
        
        return summary
    
    def _calculate_overall_risk_level(self) -> RiskLevel:
        """计算整体风险等级"""
        # 简化的风险等级计算逻辑
        risk_scores = []
        
        # 市场风险评分
        market_metrics = self.market_risk_monitor.metrics.metrics
        var_1d = market_metrics.get('var_1d', 0)
        if var_1d > 0.1:  # 10%
            risk_scores.append(4)  # CRITICAL
        elif var_1d > 0.05:  # 5%
            risk_scores.append(3)  # HIGH
        elif var_1d > 0.02:  # 2%
            risk_scores.append(2)  # MEDIUM
        else:
            risk_scores.append(1)  # LOW
        
        # 持仓风险评分
        position_metrics = self.position_risk_monitor.metrics.metrics
        concentration = position_metrics.get('concentration_risk', 0)
        if concentration > 0.5:  # 50%
            risk_scores.append(4)  # CRITICAL
        elif concentration > 0.3:  # 30%
            risk_scores.append(3)  # HIGH
        elif concentration > 0.2:  # 20%
            risk_scores.append(2)  # MEDIUM
        else:
            risk_scores.append(1)  # LOW
        
        # 计算加权平均风险等级
        if not risk_scores:
            return RiskLevel.LOW
        
        avg_score = sum(risk_scores) / len(risk_scores)
        
        if avg_score >= 3.5:
            return RiskLevel.CRITICAL
        elif avg_score >= 2.5:
            return RiskLevel.HIGH
        elif avg_score >= 1.5:
            return RiskLevel.MEDIUM
        else:
            return RiskLevel.LOW
    
    def update_market_data(self, symbol: str, price: float) -> None:
        """更新市场数据"""
        self.market_risk_monitor.update_price_data(symbol, price)
    
    def update_portfolio_value(self, value: float) -> None:
        """更新组合价值"""
        self.market_risk_monitor.update_portfolio_value(value)
    
    def update_position(self, symbol: str, quantity: float, price: float, **kwargs) -> None:
        """更新持仓信息"""
        self.position_risk_monitor.update_position(symbol, quantity, price, **kwargs)
    
    def add_risk_limit(self, limit: RiskLimit) -> None:
        """添加风险限额"""
        self.position_risk_monitor.add_risk_limit(limit)